package com.niodata.dt.fs.http;

import com.google.common.base.Splitter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;

public class HttpFileUtil {

  private static final long KB = 1024;
  private static final long MB = KB * 1024;
  private static final long GB = MB * 1024;
  private static final long TB = GB * 1024;
  private static final long _128MB = MB * 128;


  /**
   * parse http file list.
   *
   * @param document html document
   * @param parent parent dir
   * @param conf config
   * @return status list
   * @throws Exception ex
   */
  public static List<FileStatus> parseFile(Document document, String parent, Configuration conf)
        throws Exception {
    if (parent.endsWith("/")) {
      parent = parent.substring(0, parent.length() - 1);
    }
    Iterator<Element> iterator = document.getElementsByTag("pre").iterator();
    if (!iterator.hasNext()) {
      throw new IOException("html document format error:no <pre> node found");
    }
    Element pre = iterator.next();
    List<Node> nodes = pre.childNodes();
    int i = 0;
    Node node = nodes.get(i);
    while (!node.nodeName().equals("a")) {
      i++;
      node = nodes.get(i);
    }
    int j = nodes.size() - 1;
    node = nodes.get(j);
    while (!node.nodeName().equals("a")) {
      j--;
      node = nodes.get(j);
    }
    Node pathNode = null;
    Node infoNode = null;
    List<FileStatus> statusList = new ArrayList<>(nodes.size() / 2);
    for (; i <= j; i += 2) {
      pathNode = nodes.get(i);
      infoNode = nodes.get(i + 1);
      String path = pathNode.attr("href");
      if (path.contains("..")) {
        //ignore parent dir
        continue;
      }
      if (!path.startsWith("/")) {
        path = parent + "/" + path;
      }
      boolean isDir = path.endsWith("/");
      if (isDir) {
        path = path.substring(0, path.length() - 1);
      }
      if (!path.startsWith(parent)) {
        continue;
      }
      List<String> parts = Splitter.on("/").omitEmptyStrings().splitToList(path);
      String name = parts.get(parts.size() - 1);
      //time
      //length
      String timeLenStr = infoNode.outerHtml().trim();
      String[] ss = parseFileInfo(timeLenStr);
      long length = parseLength(ss[1]);
      Date lastModified = new Date(0);
      String format = conf.get(HttpFileSystem.FS_HTTP_TIME_FORMAT,
            "dd-MMM-yyyy HH:mm");
      if (ss[0].contains("-") && ss[0].length() == format.length()) {
        lastModified = parseTime(ss[0], format);
      }
      FileStatus status = new FileStatus(isDir ? 0L : length,
            isDir, 1, _128MB, lastModified.getTime(), new Path(path));
      statusList.add(status);
    }
    return statusList;
  }

  /**
   * parse http file length.
   *
   * @param lengthStr length string
   * @return length in byte
   */
  public static long parseLength(String lengthStr) {
    if (lengthStr.equals("-")) {
      return -1;
    }
    String u = lengthStr.charAt(lengthStr.length() - 1) + "";
    //无单位，作为字节数处理
    if (!"BKMGT".contains(u)) {
      return Long.parseLong(lengthStr);
    }
    lengthStr = lengthStr.toUpperCase();
    String unit = lengthStr.charAt(lengthStr.length() - 1) + "";
    String d = lengthStr.substring(0, lengthStr.length() - 1);
    double base = Double.parseDouble(d);
    double v = 0.0;
    switch (unit) {
      case "B":
        v = base;
        break;
      case "K":
        v = KB * base;
        break;
      case "M":
        v = MB * base;
        break;
      case "G":
        v = GB * base;
        break;
      case "T":
        v = TB * base;
        break;
      default:
        throw new RuntimeException("unkown unit:" + unit);
    }
    return (long) v;
  }

  public static Date parseTime(String timeStr, String timeFormat) throws Exception {
    SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormat, Locale.ENGLISH);
    Date date = (Date) dateFormat.parseObject(timeStr);
    return date;
  }

  /**
   * parse http file modiftime , length.
   *
   * @param timelenStr time&length string
   * @return array with datatime  and length string
   */
  public static String[] parseFileInfo(String timelenStr) {
    //12-Apr-2021 14:51   14M
    //12-Apr-2021 14:51   14 M
    //12-Apr-2021 14:51   0
    //12-Apr-2021 14:51   -
    timelenStr = timelenStr.trim();
    if (!timelenStr.contains(" ")) {
      return new String[]{"-", "0B"};
    }
    String len = "";
    String time = "";
    String unit = "";
    int idx = timelenStr.length() - 1;
    if (timelenStr.charAt(idx) == '-' || timelenStr.charAt(idx) == '0') {
      len = "0B";
      idx = idx - 1;
      time = timelenStr.substring(0, idx).trim();
    } else {
      unit = timelenStr.charAt(idx) + "";
      if ("BKMGT".contains(timelenStr.charAt(idx - 1) + "")) {
        unit = timelenStr.charAt(idx - 1) + "";
        idx--;
      }
      timelenStr = timelenStr.substring(0, idx).trim();
      idx = timelenStr.length() - 1;
      while (timelenStr.charAt(idx) != ' ') {
        idx--;
      }
      len = timelenStr.substring(idx).trim();
      time = timelenStr.substring(0, idx).trim();
    }

    String[] ss = new String[]{time, len + unit.toUpperCase()};
    return ss;
  }
}
